/*
 * Copyright 2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cc.siyecao.mapper.batch;

import cc.siyecao.mapper.provider.entity.EntityColumn;
import cc.siyecao.mapper.provider.entity.EntityTable;
import cc.siyecao.mapper.provider.sql.SqlScript;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.builder.annotation.ProviderContext;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 批量操作方法
 *
 */
public class BatchProvider {

    /**
     * @return 批量插入
     */
    public static String insertBatch(ProviderContext providerContext, @Param("entityList") List<?> entityList) {
        if (entityList == null || entityList.size() == 0) {
            throw new NullPointerException( "Parameter cannot be empty" );
        }
        return SqlScript.caching( providerContext, new SqlScript() {
            @Override
            public String getSql(EntityTable entity) {
                String sql = "INSERT INTO " + entity.table()
                        + "(" + entity.insertColumnList() + ")"
                        + " VALUES "
                        + foreach( "entityList", "entity", ",", () ->
                        trimSuffixOverrides( "(", ")", ",", () ->
                                entity.insertColumns().stream().map( column -> column.variables( "entity." ) ).collect( Collectors.joining( "," ) ) ) );
                return sql;
            }
        } );
    }

    /**
     * @return 批量插入
     */
    public static String insertBatchSelective(ProviderContext providerContext, @Param("entityList") List<?> entityList) {
        if (entityList == null || entityList.size() == 0) {
            throw new NullPointerException( "Parameter cannot be empty" );
        }
        return SqlScript.caching( providerContext, new SqlScript() {
            @Override
            public String getSql(EntityTable entity) {
                return "INSERT INTO " + entity.table()
                        + trimSuffixOverrides( "(", ")", ",", () -> entity.insertColumns().stream().map( column ->
                        ifTest( column.notNullTest( "entity." ), () -> column.column() + "," )
                ).collect( Collectors.joining( LF ) ) ) + LF
                        + " VALUES "
                        + foreach( "entityList", "entity", ",", () ->
                        trimSuffixOverrides( "(", ")", ",", () ->
                                entity.insertColumns().stream().map( column -> ifTest( column.notNullTest("entity."), () -> column.variables( "entity." ) + "," ) ).collect( Collectors.joining( LF ) ) ) );
            }
        } );
    }

    /**
     * @return 批量更新
     */
    public static String updateBatch(ProviderContext providerContext, @Param("entityList") List<?> entityList) {
        if (entityList == null || entityList.size() == 0) {
            throw new NullPointerException( "Parameter cannot be empty" );
        }
        return SqlScript.caching( providerContext, new SqlScript() {
            @Override
            public String getSql(EntityTable entity) {
                return foreach( "entityList", "entity", ";", "", ";", () -> "UPDATE "
                        + entity.table()
                        + " SET " + entity.updateColumns().stream().map(entityColumn -> entityColumn.columnEqualsProperty("entity.")).collect( Collectors.joining( "," ) )
                        + where( () -> entity.idColumns().stream().map( entityColumn -> entityColumn.columnEqualsProperty("entity.") ).collect( Collectors.joining( " AND " ) ) ) );
            }
        } );
    }

    /**
     * @return 批量更新非空字段
     */
    public static String updateBatchSelective(ProviderContext providerContext, @Param("entityList") List<?> entityList) {
        if (entityList == null || entityList.size() == 0) {
            throw new NullPointerException( "Parameter cannot be empty" );
        }
        return SqlScript.caching( providerContext, new SqlScript() {
            @Override
            public String getSql(EntityTable entity) {
                return foreach( "entityList", "entity", ";", "", ";", () -> "UPDATE "
                        + entity.table() + set( () -> entity.updateColumns().stream().map( column -> ifTest( column.notNullTest( "entity." ), () -> column.columnEqualsProperty( "entity." ) + "," ) ).collect( Collectors.joining( LF ) ) )
                        + where( () -> entity.idColumns().stream().map( entityColumn -> entityColumn.columnEqualsProperty("entity.") ).collect( Collectors.joining( " AND " ) ) ) );
            }
        } );
    }

    /**
     * @return 根据id批量删除
     */
    public static String deleteBatchIds(ProviderContext providerContext, @Param("idList") List<?> idList) {
        if (idList == null || idList.size() == 0) {
            throw new NullPointerException( "Parameter cannot be empty" );
        }
        return SqlScript.caching( providerContext, new SqlScript() {
            @Override
            public String getSql(EntityTable entity) {
                return "DELETE FROM " + entity.table()
                        + where( () ->
                        entity.idColumns().stream().map( entityColumn -> entityColumn.column()
                                + " IN " + foreach( "idList", "item", ",", "(", ")", () -> "#{item}" ) ).collect( Collectors.joining( " AND " ) ) );
            }
        } );
    }

    /**
     * @return 根据id批量查询
     */
    public static String selectByIds(ProviderContext providerContext, List<?> idList) {
        if (idList == null || idList.size() == 0) {
            throw new NullPointerException( "Parameter cannot be empty" );
        }
        return SqlScript.caching( providerContext, new SqlScript() {
            @Override
            public String getSql(EntityTable entity) {
                return "SELECT " + entity.baseColumnAsPropertyList()
                        + " FROM " + entity.table()
                        + where( () ->
                        entity.idColumns().stream().map( entityColumn -> entityColumn.column()
                                + " IN " + foreach( "idList", "item", ",", "(", ")", () -> "#{item}" ) ).collect( Collectors.joining( " AND " ) ) );
            }
        } );
    }


}
